/* 
 * File:   bara.cpp
 * Author: bjmey
 *
 * Based on:
 * Tcl in ~ 500 lines of code by Salvatore Antirez Sanfilippo. BSD licensed. 
 *
 * Created on 2007-10-22
 *
 */

#include <cstdio>
#include <cstdlib>
#include <string>
#include <map>
#include <vector>
#include <list>
#include <sstream>
#include <iostream>
#include <fstream>

#include "bara.h"

using std::cin;
using std::cout;
using std::cerr;
using std::endl;

/* Our string type. Normally, based on std::string, but it may also be overridden to a different string type. */
#ifndef BARA_STRING
	typedef std::string Barastring;
#else 
	typedef BARA_STRING Barastring;
#endif

typedef std::vector<Barastring> Barastringvector;

struct Baratoken {
	Barastring 		_text;
	Baratokenkind 	_kind;
};

struct Baraparser {
	
	Barastring 		_text;
	Barastring 		_separator;		/* What separator to use */
	Barastring 		_variablename;	/* How to parse a variable name */
	size_t 			_pos; 			/* Current text position */
	size_t 			_len; 			/* Remaining length */
	size_t 			_start;  		/* Token start */
	size_t 			_end;    		/* Token end */
    Baratokenkind 	_type; 			/* Token type, BARA_TOKEN_... */
    int 			_insidequote; 	/* True if inside " " */
	
	void Baraparser::init(Barastring str);
	int Baraparser::get_token(Baratoken & token);
	
};


struct Baravariable {
    Barastring _name; 
	Barastring _value;
	double     _numval;
	void Baravariable::init(const Barastring & name, const Barastring & value); 
};

typedef Baravariable * Baravariableptr;

typedef std::map<Barastring, Baravariableptr> Baravariablemap;

struct Bararunner; /* Forward declaration  of interpreter */

struct Baracommanddata {
	Barastringvector _vector;
};
/* Extend this class for your own functions, if need be. */

typedef int (*Baracommandfunction)(Bararunner *i, Barastringvector argv, Baracommanddata *privdata);


struct Baracommand {
    Barastring 			_name;
    Baracommandfunction _func;
    Baracommanddata *	_privdata;
	void Baracommand::init(const Barastring & name, Baracommandfunction f, Baracommanddata *privdata);
	Baracommand() {
		_privdata = NULL;
	}
	~Baracommand() {
		delete _privdata;
	}
};

typedef Baracommand * Baracommandptr;

typedef std::map<Barastring, Baracommandptr> Baracommandmap;

template <class I> class tree
{
   public:
   // Tree Item properties
   I item;
   // Children items
   std::vector< tree<I> > children;
};

struct Baracallframe {
	Baravariablemap _variables;
    Baracallframe *_parent; /* parent is NULL at top level */
	int _depth;
};

typedef Baracallframe * Baracallframeptr;

struct Bararunner {	
    int 			_level; /* Level of nesting */
    Baracallframe  *_callframe;
	std::vector < Baracallframe * > _listframe;
	tree   < Baracallframe > _treeframe;
    Baracommandmap	_commands;
    Barastring 		_result;
	void Bararunner::init();
	void Bararunner::set_result(const Barastring & res);
    Barastring Bararunner::get_result() { return this->_result; }
	
	int Bararunner::drop_callframe(); 
	int Bararunner::add_callframe();
	Baracallframeptr Bararunner::get_callframe(int uplevel = 0);
	Baravariableptr Bararunner::get_var(const Barastring & name, int uplevel = 0); 
	int Bararunner::set_var(const Barastring & name, const Barastring & val, int uplevel = 0);

	Baracommandptr  Bararunner::get_command(const Barastring & name); 
	int Bararunner::set_command(const Barastring & name, Baracommandfunction f, Baracommanddata *privdata);
	int Bararunner::wipe_command(const Barastring & name);
	
	Barastring Bararunner::escape(Barastring str);
	int Bararunner::eval(const Barastring & text);
	int Bararunner::arity_error(Barastring name, int arity, int got);
	int Bararunner::check_arity(Barastringvector argv, int arity); 
	int Bararunner::check_arity(Barastringvector argv, int arity1, int arity2); 
	

	
	
	void Bararunner::register_core_commands();
	void Bararunner::run_file(const char * filename);
	void Bararunner::run_interactive();
	
	Barastring Bararunner::interpolate(Barastring string); 
	int Bararunner::call_command(Barastringvector argv); 
	Barastring Bararunner::handle_argument(Barastring arg);


	static int Bararunner::to_int(Barastring s); 
	static double Bararunner::to_double(Barastring s);
	static Barastring Bararunner::to_string(const int value); 
	static Barastring Bararunner::to_string(const double value);
	static Barastringvector 
	Bararunner::split_any(const Barastring i, const Barastring separators); 
	
	static int Bararunner::command_if(Bararunner *i, Barastringvector argv, Baracommanddata *pd); 
	static int Bararunner::command_set(Bararunner *i, Barastringvector argv, Baracommanddata *pd); 
	static int Bararunner::command_upset(Bararunner *i, Barastringvector argv, Baracommanddata *pd);
	static int Bararunner::command_math(Bararunner *i, Barastringvector argv, Baracommanddata *pd); 
	static int Bararunner::command_puts(Bararunner *i, Barastringvector argv, Baracommanddata *pd); 
	static int Bararunner::command_while(Bararunner *i, Barastringvector argv, Baracommanddata *pd); 	
	static int Bararunner::command_retcodes(Bararunner *i, Barastringvector argv, Baracommanddata *pd);
	static int Bararunner::command_callproc(Bararunner *i, Barastringvector argv, Baracommanddata *pd);
	static int Bararunner::command_proc(Bararunner *i, Barastringvector argv, Baracommanddata *pd);
	static int Bararunner::command_delproc(Bararunner *i, Barastringvector argv, Baracommanddata *pd);
	static int Bararunner::command_return(Bararunner *i, Barastringvector argv, Baracommanddata *pd);
	
};


void Baraparser::init(Barastring str) {
	_text 			= str;
	_len  			= _text.size();
	_start 			= 0; 
	_end 			= 0; 
	_insidequote 	= 0;
	_pos            = 0;
	_type 			= BARA_TOKEN_EOL;
} 

int Baraparser::get_token(Baratoken & token) {
	int result  = BARA_OK;
	return result;	
}

void Baravariable::init(const Barastring & name, const Barastring & value) {
	_name 	= name;
	_value 	= value;
}

void Baracommand::init(const Barastring & name, Baracommandfunction f, Baracommanddata *privdata) {
    _name = name;
    _func = f;
    _privdata = privdata;
}

void Bararunner::init() {
    _level 		= 0;
	add_callframe();
    set_result("");
}

void Bararunner::set_result(const Barastring & res) {
    _result = res;
}

int Bararunner::drop_callframe() {
	if(_listframe.size() < 2) return BARA_ERR;
	Baracallframe *cf = _listframe.back();
	/*
    Baracallframe *cf = this->_callframe;
	if(!cf || !cf->_parent) return BARA_ERR;
	*/
	for(	Baravariablemap::iterator index = cf->_variables.begin(); 
			index != cf->_variables.end(); ++index) {
		delete index->second;
	}
	cf->_variables.clear();
    // this->_callframe = cf->_parent;
	_listframe.pop_back();
    delete cf;
	return BARA_OK;
}

int Bararunner::add_callframe() {
	Baracallframe * cf 		= new Baracallframe();
	cf->_variables.clear();
	cf->_depth 				= _listframe.size();
	_listframe.push_back(cf);
	return BARA_OK;
}

// Look for a callframe uplevel levels above this one.
Baracallframeptr Bararunner::get_callframe(int uplevel) {
	if( uplevel >= _listframe.size() ) return NULL;
	if( uplevel < 0 ) 				   return NULL;
	int size = _listframe.size();
	Baracallframeptr result = _listframe[size - uplevel - 1];
	return result;
}

Baravariableptr Bararunner::get_var(const Barastring & name, int uplevel) {
	Baravariableptr  varptr = NULL;
	Baracallframeptr cf = get_callframe(uplevel);
	if(!cf) return NULL; // We couldn't find that call frame.
	// Here we should have advanced to the right callframe. 
	varptr = cf->_variables[name];
    return varptr;
}


int Bararunner::set_var(const Barastring & name, const Barastring & val, int uplevel) {
    Baravariable *   var = get_var(name);
    if (var) {
        var->_value = val;
    } else {
		Baracallframeptr cf = get_callframe(uplevel);
		if(!cf) { // Stackframe not found.
			cerr << "Uplevel " << uplevel << " is too high." << endl;
			return BARA_ERR;
		}
        var = new Baravariable();
		var->init(name, val);
		cf->_variables[name] = var;
    }
    return BARA_OK;
}

Baracommandptr Bararunner::get_command(const Barastring & name) {
	return _commands[name];
}

int Bararunner::set_command(const Barastring & name, Baracommandfunction f, Baracommanddata *privdata) {
    Baracommand * comm = get_command(name);
    Barastring errbuf;
    if (comm) {
        errbuf = "Command " + name + " already defined";
        set_result(errbuf);
        return BARA_ERR;
    }
    comm = new Baracommand();
	comm->init(name, f, privdata);
	_commands[name] = comm;
    return BARA_OK;
}

int Bararunner::wipe_command(const Barastring & name) {
    Baracommand * comm = get_command(name);
    Barastring errbuf;
    if (!comm) {
        errbuf = "Command " + name + " not defined";
        set_result(errbuf);
        return BARA_ERR;
    }
	_commands.erase(name); // Remove the command from the command name map.
	delete comm;
    return BARA_OK;
}


/* XXX: implement this stub */
Barastring Bararunner::escape(Barastring str) {
  return str;
}

struct Barahelper {
	Barastringvector 		_argv;
	Barastring::size_type	 _pos;
};

typedef std::vector<Barahelper> Barahelpervector;


Barastring::size_type find_matching
	(Barastring input , Barastring::size_type start,  Barastring open, Barastring close, Barastring escape) {
	Barastring::size_type pos 	= 0;
	Barastring::size_type size	= input.size();
	int level = 1;
	Barastring pair = open + close;
	while(pos < size) { 
		pos = input.find_first_of(pair, start );
		if(pos == Barastring::npos) return Barastring::npos; // not found.
		Barastring pre, now;
		if( pos > 0 ) pre = input.substr(pos - 1, 1); else pre = "\0";
		now = input.substr(pos, 1);
		if(pre == escape) { 
				pos++; // skip escaped open or close quote/paren
		} else {
			if (now == close) {
				level--; pos++;
			} else if(now == open) { // order is important, in case open == close
				level++; pos++;
			}  
		}
		if (level <= 0 ) { return pos; }
		start = pos;
	}
	return Barastring::npos; // not found.
}


Barastring string_escape(const Barastring & input) {
	Barastring result 			= input;
	Barastring::size_type pos 	= result.find_first_of("\\");
	while(pos != Barastring::npos && (pos + 1) < result.size() ) {
		
		Barastring::value_type esc = result[pos + 1];
		Barastring::value_type rep = esc;
		switch(esc) {
			case 'e': rep = '\e'; break;
			case 'n': rep = '\n'; break;
			case 'r': rep = '\r'; break;
			case 't': rep = '\t'; break;
			case '0': rep = '\0'; break;
			case '\\': rep = '\\'; break;
		}
		result.replace(pos + 1, 1, 1, rep);
		result.erase(pos,1);
		pos = result.find_first_of("\\", pos + 1);
	}
	return result;
}


Barahelper eval_get_command(const Barastring & input, Barastring::size_type first = 0) {
	Barastring::size_type pos 	= first;
	Barastring::size_type start = pos;
	Barastring::size_type stop  = pos;
	Barastring::size_type size	= input.size();
	Barastring startpart 		= input.substr(0,1);
	bool done = false;
	Barahelper result;
	result._pos = pos;

	Barastring aid;
	while((pos < size) && (pos != Barastring::npos)) { 
		start 						= input.find_first_not_of(BARA_SEPARATOR, pos);
		if(start == Barastring::npos) { 
			result._pos = size; // We're all through with this string.
			return result;
			// We can't find any more parts of this string. Get out of here.		
		}	
		
		Barastring startpart 		= input.substr(start,1);
		if(startpart == "{") 		{
			pos = find_matching(input, start + 1, "{" , "}" , "\\");
		} else if (startpart=="[") 	{
			pos = find_matching(input, start + 1, "[" , "]" , "\\");
		} else if (startpart=="\"") {
			pos = find_matching(input, start + 1, "\"", "\"", "\\");
		} else if (startpart=="#")  {
			pos = input.find_first_of("\n", start + 1);
		} else if (startpart=="\n" || startpart==";") { 
			// End of command
			result._pos = pos + 1;
			// cout << "end of command at: " << result._pos << endl;
			return result;
		} else {
			pos = input.find_first_of(BARA_SEPARATOR, start);
		}
		if (pos == Barastring::npos) { stop = size; } else { stop = pos; }
		aid = input.substr(start, stop - start);
		result._argv.push_back(string_escape(aid));
		result._pos = pos;
	}
	return result;
}

Barahelpervector eval_get_commands(const Barastring & input) {
	Barahelpervector result;
	Barastring::size_type pos 	= 0;
	Barastring::size_type size	= input.size();
	Barastring startpart 		= input.substr(0,1);
	bool done = false;
	Barahelper aid;
	while(pos < size) {
		aid = eval_get_command(input, pos);
		result.push_back(aid);
		pos = aid._pos;
		if (pos == Barastring::npos) return result;
	}	
	return result;
}

Barastring Bararunner::interpolate(Barastring string) {
	Barastring result = string;
	Barastring marker = "$";
	Barastring::size_type pos 	= result.find_first_of(marker);
	while(pos != Barastring::npos && (pos + 1) < result.size() ) {
		Barastring::size_type stop = result.find_first_not_of(BARA_VAR_NAME, pos + 1);
		if(stop==Barastring::npos) { stop = result.size(); }		 
		Barastring name = string.substr(pos + 1, stop - pos - 1);
		// name of the variable
		Baravariable * var = get_var(name);
		Barastring replace_by = "";
		if(var) { replace_by = var->_value; } else { cerr << "Warning: variable not found: " << name << endl; }
	    // Unknown variables are replaced by nothing for now. 
		result.replace(pos, stop - pos, replace_by);
		pos = result.find_first_of(marker, pos + 1);
	}
	return result;
}

int Bararunner::call_command(Barastringvector argv) {
	Baracommand *c;
	if (argv.size() > 0) {
		if ((c = get_command(argv[0])) == NULL) {
			Barastring errbuf = "No such command: '"  + argv[0]  + "'";
			set_result(errbuf);
			return BARA_ERR;
		} 
		int retcode = c->_func(this, argv, c->_privdata);
		return retcode;
	}
	return BARA_ERR;
}

Barastring Bararunner::handle_argument(Barastring arg) {
	Barastring::size_type size = arg.size();
	if(size<2) return arg;
	Barastring::value_type first = arg[0];
	switch(first) {
		case '\"': return interpolate(arg.substr(1, size - 2));
		case '{' : return arg.substr(1, size - 2);
		case '[' : eval(interpolate(arg.substr(1, size - 2)));  return get_result();
		case '#' : return "#";
	    default  : return interpolate(arg);
	}	
}

/* EVAL! */
int Bararunner::eval(const Barastring & text) {
    int retcode = BARA_OK;
    this->set_result("");
		
	Barahelpervector helpvec = eval_get_commands(text); 
	for (int comind = 0; comind < helpvec.size() ; comind ++) {
		Barastringvector argvin = helpvec[comind]._argv;
		Barastringvector argvout;
		for (int index =0; index < argvin.size() ; index ++) {
			Barastring proc = handle_argument(argvin[index]);
			if(proc != "#") { // XXX: Not a good way to handle comments.
			  argvout.push_back(proc);  
			}  
		}
		retcode  = call_command(argvout);
		if(retcode == BARA_ERR) { return retcode; }
		else if (retcode == BARA_BREAK) { return retcode; }
	}
	return retcode;
}

int Bararunner::arity_error(Barastring name, int arity, int got) {
    std::ostringstream errbuf;
    errbuf << "Wrong number of args for " << name << ": " << got << " in stead of " << arity ;
    set_result(errbuf.str());
    return BARA_ERR;
}

int Bararunner::check_arity(Barastringvector argv, int arity1, int arity2) {
	int argc = argv.size();
	if ((argc != arity1) && (argc != arity2)) return arity_error(argv[0], arity1, argc);
	return BARA_OK;
}


int Bararunner::check_arity(Barastringvector argv, int arity) {
	int argc = argv.size();
	if (argc != arity) return arity_error(argv[0], arity, argc);
	return BARA_OK;
}

int Bararunner::to_int(Barastring s) {
	return atoi(s.c_str());
}

double Bararunner::to_double(Barastring s) {
	return atof(s.c_str());
}

Barastring Bararunner::to_string(const double value) {
	std::ostringstream os;
	os << value;
	return os.str();
}

Barastring Bararunner::to_string(const int value) {
	std::ostringstream os;
	os << value;
	return os.str();
}

int Bararunner::command_set(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
	int err = i->check_arity(argv, 2, 3); if (err != BARA_OK) { return err; }   
	if (argv.size() == 2) {
		Baravariable * var = i->get_var(argv[1]);
		if(!var) { 
			cerr << "No such variable: " + argv[1]; return BARA_ERR; 
		}
		i->set_result(var->_value);
		return BARA_OK;
	}
    i->set_var(argv[1], argv[2]);
    i->set_result(argv[2]);
    return BARA_OK;
}

int Bararunner::command_upset(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
	int err = i->check_arity(argv, 3, 4); if (err != BARA_OK) { return err; }   
	int uplevel =0;
	try { 
		uplevel = to_int(argv[1]);
	} catch (...) {
		cerr << "Uplevel must be an integer." <<endl;
		return BARA_ERR;
	}	
	if (argv.size() == 3) {
		Baravariable * var = i->get_var(argv[2], uplevel);
		if(!var) { 
			cerr << "No such variable: " << argv[2] << " in uplevel" << uplevel << endl; 
			return BARA_ERR; 
		}
		i->set_result(var->_value);
		return BARA_OK;
	}
    i->set_var(argv[2], argv[3], uplevel);
    i->set_result(argv[3]);
    return BARA_OK;
}


int Bararunner::command_math(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
	double a, b, c; char op, op2;
	int err = i->check_arity(argv, 3); if (err != BARA_OK) return err;
    a = to_double(argv[1]); b = to_double(argv[2]); op = argv[0][0]; op2 = argv[0][1];
    if (op == '+') c = a+b;
    else if (op == '-') c = a-b;
    else if (op == '*') c = a*b;
    else if (op == '/') c = a/b;
    else if (op == '>' && argv[0][1] == '\0') c = a > b;
    else if (op == '>' && op2 == '=')  c = a >= b;
    else if (op == '<' && op2 == '\0') c = a < b;
    else if (op == '<' && op2 == '=')  c = a <= b;
    else if (op == '=' && op2 == '=')  c = a == b;
    else if (op == '!' && op2 == '=')  c = a != b;
    else c = 0;
    i->set_result(to_string(c));
    return BARA_OK;
}

Barastring barastringvector_join(Barastringvector vec, Barastring join) {
	Barastring result = "";
	for(int index = 0; index < vec.size(); index ++) {
		if(index != 0) result += join; // Put in the joining string.
		result += vec[index]; 
	} 
	return result;
}

template <class V> V vector_copy(V & vec, int first, int last=-1) {
	V result;
	last 	= (last > vec.size() ? vec.size() : last);
	first 	= (first < 0 ?  0 : first);
	for(int index = first; index < last; index ++) {
		result.push_back(vec[index]);
	}
	return result;
}

int Bararunner::command_puts(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
	argv = vector_copy(argv,1, -1);
    cout << barastringvector_join(argv, " ") << "\n";
    return BARA_OK;
}

int Bararunner::command_if(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
    int retcode;
	int err = i->check_arity(argv, 3, 5); if (err != BARA_OK) return err;
    if ((retcode = i->eval(argv[1])) != BARA_OK) return retcode;
    if (to_int(i->get_result())) 	return i->eval(argv[2]);
    else if (argv.size() == 5) 	return i->eval(argv[4]);
    return BARA_OK;
}

int Bararunner::command_while(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
	int err = i->check_arity(argv, 3); if (err != BARA_OK) return err;
    while(1) {
        int retcode = i->eval(argv[1]);
        if (retcode != BARA_OK) return retcode;
        if (to_int(i->get_result())) {
			retcode = i->eval(argv[2]);
            if (retcode == BARA_CONTINUE) continue;
            else if (retcode == BARA_OK) continue;
            else if (retcode == BARA_BREAK) return BARA_OK;
            else return retcode;
        } else {
            return BARA_OK;
        }
    }
}

int Bararunner::command_retcodes(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
	int err = i->check_arity(argv, 1); if (err != BARA_OK) return err;
    if (argv[0] == "break") return BARA_BREAK;
    else if (argv[0] == "continue") return BARA_CONTINUE;
    return BARA_OK;
}

	
Barastringvector Bararunner::split_any(const Barastring i, const Barastring separators)  {
	Barastringvector list;
	Barastring::size_type lastpos = 0;
	Barastring::size_type pos 	  = i.find_first_of(separators, lastpos);
	while(pos != Barastring::npos) {
		Barastring sub 	= i.substr(lastpos, pos - lastpos );
		list.push_back(sub);
		lastpos 		= pos + 1;
		pos 			= i.find_first_of(separators, lastpos);
	}
	Barastring sub 	= i.substr(lastpos, i.size() - lastpos );
	list.push_back(sub);
	// Don't forget to put the last part of the string in the list.
	return list;
}


int Bararunner::command_callproc(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
    Barastringvector extra	= pd->_vector;
    Barastring alist 		= extra[0], body = extra[1], paramstr = alist;
    int arity = 1, done 	= 0, errcode = BARA_OK;
    Barastring errbuf;
	i->add_callframe();
	Barastring sep = " ";
	Barastringvector paramhelper = Bararunner::split_any(paramstr, sep);
	for(int index = 0; index < paramhelper.size(); index ++) {
		Barastring paramname = paramhelper[index];
		if(paramname.size() < 1) continue;
		if(arity >= argv.size()) { 
			cerr << "Function called with too few arguments." <<endl;
			return BARA_ERR;
		}	// No error for too many arguments. Silently discard them. 
		i->set_var(paramname, argv[arity]);
		arity++;
	} 
    // if (arity != (argv.size() - 1) ) goto arityerr;
    errcode = i->eval(body);
    if (errcode == BARA_RETURN) errcode = BARA_OK;
    	i->drop_callframe(); /* remove the called proc callframe */
    return errcode;
}

int Bararunner::command_proc(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
    Baracommanddata * procdata;
	procdata = new Baracommanddata();
	int err  = i->check_arity(argv, 4); if (err != BARA_OK) return err;
    procdata->_vector.push_back(argv[2]); /* arguments list */
    procdata->_vector.push_back(argv[3]); /* procedure body */
	cout << "Making proc: " << argv[1] << ":" << argv[2] << ":" << argv[3] << endl;
    int result = i->set_command(argv[1], Bararunner::command_callproc, procdata);
	return result;
}

int Bararunner::command_delproc(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
	int err  = i->check_arity(argv, 2); if (err != BARA_OK) return err;
	int result = i->wipe_command(argv[1]);	
	return result;
}

int Bararunner::command_return(Bararunner *i, Barastringvector argv, Baracommanddata *pd) {
	int err = i->check_arity(argv, 1, 2); if (err != BARA_OK) return err;
    i->set_result((argv.size() == 2) ? argv[1] : "");
    return BARA_RETURN;
}

void Bararunner::register_core_commands() {
    int j; const char *names[10] = {"+","-","*","/",">",">=","<","<=","==","!="};
    for (j = 0; j < 10; j++) {
		Barastring name = names[j] ;
        set_command(name, Bararunner::command_math, NULL);
	}
    set_command("set", 		Bararunner::command_set			, NULL);
	set_command("upset",    Bararunner::command_upset	    , NULL);
    set_command("puts", 	Bararunner::command_puts		, NULL);
    set_command("if", 		Bararunner::command_if			, NULL);
    set_command("while",	Bararunner::command_while		, NULL);
    set_command("break",	Bararunner::command_retcodes	, NULL);
    set_command("continue",	Bararunner::command_retcodes	, NULL);
    set_command("proc",		Bararunner::command_proc		, NULL);
	set_command("def",		Bararunner::command_proc		, NULL);
	set_command("undef",	Bararunner::command_delproc		, NULL);
    set_command("return",	Bararunner::command_return		, NULL);
}

void Bararunner::run_file(const char * filename) {
	std::string 	line  ="";
	std::string 	script="";
	std::ifstream file(filename);
	if (file.is_open()) {
		while (!file.eof() ) {
			getline(file, line);
			cout << line << endl;
			script += line;
		}
		file.close();
	} else {
		cerr << "bara: could not open " << filename << "\n";
	}
	if (eval(script) != BARA_OK) 
		cerr << "bara: " << get_result() << "\n";
}

void Bararunner::run_interactive() {
	while(cin.good()) {
     	std::string input;
        int retcode;
        cout << "bara> " << std::flush;
		getline(cin, input);
        retcode = this->eval(input);
		Barastring result = this->get_result();
        if (result.size() > 0) {
        cout << result << endl << std::flush; }
  	}
}

#ifdef BARA_MAIN

int main(int argc, char **argv) {
	Barastring tsts;
	tsts = "0123456789";
    Bararunner inter;
    inter.init();
    inter.register_core_commands();
    if (argc == 1) {
		inter.run_interactive();
    } else if (argc == 2) {
		inter.run_file(argv[2]);
    }
    return 0;
}

#endif














